#include <stdio.h>
#include <iostream>

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <drm.h>
#include <drm_fourcc.h>
#include <errno.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <linux/stddef.h>
#include <linux/videodev2.h>
#include <stddef.h>

#include "mpp_common.h"
#include "mpp_itc_display_interface.h"
#include "mpp_itc_rga.h"
// #include "bo.h"
// #include "dev.h"
// #include "modeset.h"


// #include "mpp_display_buffer.h"
// #include "mpp_screens.h"
// #include "mpp_showing.h"


#include "display_dev.h"
#include "screens.h"
#include "display_buffer.h"

using namespace std;



// static int src_format = V4L2_PIX_FMT_NV12;
// static int dst_format = V4L2_PIX_FMT_NV12;


typedef struct _overlayParam {
	unsigned char *bm_buff; //base map buff
	int bm_w; //base map width
	int bm_h; //base map height

	unsigned char *top_buff; //top overlay buff
	int top_w;
	int top_h;

	int pos_x; //position x [0, bm_w-top_w]
	int pos_y;
}overlayParam;

static void dump_overlayparam(overlayParam *param)
{
	printf("=== overlayParam ===\n");
	printf("\tbm_buff:%p\n", param->bm_buff);
	printf("\tbm_w:%d\n", param->bm_w);
	printf("\tbm_h:%d\n\n", param->bm_h);
	printf("\ttop_buff:%p\n", param->top_buff);
	printf("\ttop_w:%d\n", param->top_w);
	printf("\ttop_h:%d\n\n", param->top_h);
	printf("\tpos_x:%d\n", param->pos_x);
	printf("\tpos_y:%d\n", param->pos_y);
}

/* 功能：nv12图像叠加 */
static int pixel_format_nv12_overlay(overlayParam *param)
{
	unsigned char *rect_y = NULL;
	unsigned char *rect_u = NULL;
	unsigned char *rect_v = NULL;
	unsigned char *rect_tmp_y = NULL;
	unsigned char *rect_tmp_u = NULL;
	unsigned char *rect_tmp_v = NULL;
	unsigned char *top_y = NULL;
	unsigned char *top_u = NULL;
	unsigned char *top_v = NULL;
	int i, j;

	//dump_overlayparam(param);

	if (!param || !(param->bm_buff) || !(param->top_buff) ||
		((param->bm_w * param->bm_h) <= 0) || ((param->top_w * param->top_h) <= 0) ||
		(param->pos_x < 0) || (param->pos_y < 0)) {
		printf("ERROR: %s input args invalid!\n", __func__);
		return -EINVAL;
	}

	/* Position align */
	param->pos_x = (param->pos_x / 2) * 2;
	param->pos_y = (param->pos_y / 2) * 2;

	if (((param->pos_x + param->top_w) >= param->bm_w) ||
		(((param->pos_y + param->top_h) >= param->bm_h))) {
		printf("ERROR: %s overlay img size invalid!\n", __func__);
		return -EINVAL;
	}

	/*
	 * 以(pos_x, pos_y)为起始点分别向右（ｘ轴）和向下（ｙ轴）画一个矩形框（图片左上角为原点），
	　* 矩形框的款高就是顶图的宽高。该矩形框便是需要替换成顶图的区域。
	 */
	rect_y = param->bm_buff + param->pos_y * param->bm_w + param->pos_x;
	rect_u = param->bm_buff + param->bm_w * param->bm_h + param->pos_y * param->bm_w / 2 + param->pos_x;
	rect_v = rect_u + 1;
	top_y = param->top_buff;
	top_u = param->top_buff + param->top_w * param->top_h;
	top_v = top_u + 1;

	for (i = 0; i < param->top_h; i++) {
		/* 在底图中的需要overlay的矩形框内 */
		rect_tmp_y = rect_y + i * param->bm_w;
		if (i % 2 == 0) {
			rect_tmp_u = rect_u + i * param->bm_w / 2;
			rect_tmp_v = rect_tmp_u + 1;
		}

		for (j = 0; j < param->top_w; j++) {
			/* Replace y value */
			*(rect_tmp_y + j) = *(top_y + i * param->top_w + j);

			if ((i % 2 == 0) && (j % 2 == 0)) {
				*rect_tmp_u = *top_u;
				top_u += 2;
				rect_tmp_u += 2;
			} else if ((i % 2 == 1) && (j % 2 == 1)) {
				*rect_tmp_v = *top_v;
				top_v += 2;
				rect_tmp_v += 2;
			}
		}
	}

	return 0;
}

class mpp_mutex
{
    private:
        pthread_mutex_t *mutex ; 
     public:
        mpp_mutex(pthread_mutex_t * mux):mutex(NULL)
        {
                mutex = mux;
                if(mutex)
                    pthread_mutex_lock(mutex);
        }
        ~mpp_mutex()
        {
            if(mutex)
                    pthread_mutex_unlock(mutex);
        }
};

class mpp_display
{
    public:
        mpp_display();
        ~mpp_display();
        RET_CODE mpp_display_init(uint32_t width ,uint32_t height,uint32_t vrefresh);
        MPP_SCREEN_ATTR *mpp_display_get_screen_attr();
        MPP_SURFACE_ATTR *mpp_display_create_surface(FRAME_TYPE format,uint32_t position_x,uint32_t position_y,uint32_t width,uint32_t heigh);
        RET_CODE mpp_display_draw(MPP_SURFACE_ATTR *surface,
                                                        uint32_t position_x ,uint32_t position_y,
                                                        uint32_t width ,uint32_t height,uint32_t BufferIndex = 0);
        RET_CODE mpp_surface_create_rects(MPP_SURFACE_ATTR *surface,
                                                                    uint32_t count_rects,MPP_SURFACE_RECT_S  *rect);
        RET_CODE mpp_surface_rect_draw(MPP_SURFACE_ATTR *surface,
                                                            uint32_t index,void *data,uint32_t width ,uint32_t height);
        RET_CODE mpp_surface_destroy_rects(MPP_SURFACE_ATTR *surface);                                                    

        RET_CODE mpp_display_destroy_surface(MPP_SURFACE_ATTR **surface);
        RET_CODE mpp_display_deinit();
    private:

        // mpp_screens *m_screens_handle;
        // mpp_showing *show;

        display_buffer *buffer;
        MPP_SCREEN_ATTR mScreen;
        screens *screen;//(&buffer,width,height,vrefresh);
        uint8_t *mDisPlayProcessBuffer;

        MPP_IMAGE_PROCESS_T * mProcess;
        pthread_mutex_t mMutex = PTHREAD_MUTEX_INITIALIZER; 
    private:

   
};
mpp_display::mpp_display():buffer(NULL),screen(NULL),mProcess(NULL),mDisPlayProcessBuffer(NULL)//:m_screens_handle(NULL),show(NULL)
{
    buffer = new display_buffer();
    mProcess = create_image_process();   
}
mpp_display::~mpp_display()
{
    if(screen){
        delete screen;
        screen = NULL;
    }
    if(buffer){
        delete buffer;
        buffer = NULL;
    }
    if(mProcess){
        image_process_destroy(mProcess);
        mProcess = NULL;
    }
    if(mDisPlayProcessBuffer){
        delete mDisPlayProcessBuffer;
        mDisPlayProcessBuffer = NULL;
    }
}

/*************************************************对外接口***************************************************/
RET_CODE mpp_display::mpp_display_init(uint32_t width ,uint32_t height,uint32_t vrefresh)
{
    if(screen){
        delete screen;
        screen = NULL;
    }    
    if(!screen){
        if(buffer)
            screen = new screens(buffer,CODEC_ALIGN(width,2),CODEC_ALIGN(height,2),vrefresh);
        // if(screen)
        //    screen->drow_background("../../../res/Zoom1233333_1920x1080.NV12",DRM_FORMAT_NV12);

        memset(&this->mScreen,0x00,sizeof(MPP_SCREEN_ATTR));
        this->mScreen.height = CODEC_ALIGN(height,4);
        this->mScreen.width = CODEC_ALIGN(width,4);
        this->mScreen.vrefresh = vrefresh;

        if(mDisPlayProcessBuffer){
            delete mDisPlayProcessBuffer;
            mDisPlayProcessBuffer = NULL;
        }else{
            mDisPlayProcessBuffer = new uint8_t[width*height*4];
        }
        cout<< "cureen screen height: "<<this->mScreen.height <<" cureen screen width: "<<this->mScreen.width<<endl;
        
    }
    return MPP_RET_CODE_OK;
}

MPP_SCREEN_ATTR *mpp_display::mpp_display_get_screen_attr()
{
    if(screen){
        return &mScreen;
    }
}

MPP_SURFACE_ATTR * mpp_display::mpp_display_create_surface(FRAME_TYPE format,uint32_t position_x,uint32_t position_y,uint32_t width,uint32_t height)
{
    uint32_t fmt = 0;
    
    switch (format)
    {
        case MPP_FRAME_TYPE_YUV420SP:     /* YYYY... UV... (NV12)     */
            fmt= DRM_FORMAT_NV12;
            break;
        case MPP_FRAME_TYPE_YUV422:       /* YYYY... UVUV... (NV16)   */
            fmt=DRM_FORMAT_NV16;
            break;
        case MPP_FRAME_TYPE_YUV420SP_VU:  /* YYYY... VUVUVU... (NV21) */
            fmt=DRM_FORMAT_NV21;
            break;
        case MPP_FRAME_TYPE_YUV422SP_VU:  /* YYYY... VUVUVU... (NV61) */
            fmt=DRM_FORMAT_NV61;
            break;
        default:
            return NULL;
    }
    
    MPP_SURFACE_ATTR *attr   = new MPP_SURFACE_ATTR;
    if(attr && screen){
        
        //memset(attr,0x00,sizeof(MPP_SURFACE_ATTR));
        attr->format = fmt;
        attr->position_x = position_x;
        attr->position_y = position_y;
        attr->height =  CODEC_ALIGN(height,4);
        attr->width = CODEC_ALIGN(width,4);
        attr->fd = -1;
        attr->count_framebuffer = 2;
        attr->framebuffer[0] = screen->display_getImgAddr(fmt,0);
        attr->framebuffer[1] = screen->display_getImgAddr(fmt,1);
        attr->rect = NULL;
        attr->count_rect = 0;
        return attr;
    }else{
        if(attr)
            delete attr;
    }
    return NULL;
        

}
RET_CODE mpp_display::mpp_surface_create_rects(MPP_SURFACE_ATTR *surface,uint32_t count_rects,MPP_SURFACE_RECT_S  *rect)
{
    MPP_SURFACE_RECT_S  *tmp = NULL;
    if(!surface || !rect)
        return MPP_RET_CODE_FAILURE;
    
     tmp = (MPP_SURFACE_RECT_S *)calloc(count_rects, sizeof(MPP_SURFACE_RECT_S));
     if(!tmp){
         cout<<"calloc err "<<endl;
         return MPP_RET_CODE_FAILURE;
     }
     memcpy(tmp,rect,sizeof(MPP_SURFACE_RECT_S)*count_rects);    
    
    surface->count_rect = count_rects;
    surface->rect = tmp;
    cout<<" screen height "<<this->mScreen.height<< " screen width "<<this->mScreen.width<<endl;
    
    return MPP_RET_CODE_OK;
} 
RET_CODE mpp_display::mpp_surface_rect_draw(MPP_SURFACE_ATTR *surface,uint32_t index,void *data,uint32_t width ,uint32_t height)
{
     MPP_SURFACE_RECT_S  *rect = NULL;
     IMAGE_PROCESS_INFO_T   processInfo ;
     uint32_t count_rects = 0,uv_offset;
     uint8_t *framebuffer = (uint8_t *)surface->framebuffer[0];
     uint8_t *frame = (uint8_t *)data;

     size_t dataSize  = 0,dstSize = 0;
     uint32_t y=0,x = 0,framebuffer_index  = 0;
    
    
    
    if(!surface || !surface->rect)
        return MPP_RET_CODE_FAILURE;

    //cout<<"index draw "<<index <<"  surface->count_rect"<<surface->count_rect<<endl;    
    if(index >= surface->count_rect)
        return MPP_RET_CODE_FAILURE;

    if(surface->format == DRM_FORMAT_NV12 || DRM_FORMAT_NV21 ){
        dataSize = (width*height)*3/2;
        dstSize =   CODEC_ALIGN(surface->rect[index].u32Width-4,4)*CODEC_ALIGN(surface->rect[index].u32Height-4,4)*3/2;
    }else {
        dataSize = (width*height)*2;
        dstSize =  CODEC_ALIGN(surface->rect[index].u32Width-4,4)*CODEC_ALIGN(surface->rect[index].u32Height-4,4)*2;
    }

        // if(surface->count_rect > 4  && index < 5){
        //     framebuffer = (uint8_t *)surface->framebuffer[0];
        //     framebuffer_index = 0;
        // }else  if(surface->count_rect > 4  && index >= 5){
            // framebuffer = (uint8_t *)surface->framebuffer[1];
            // framebuffer_index = 1;
        // }


        //    mpp_mutex _mutex(&mMutex);

        mProcess->image_process_init(mProcess->ctx,
                                    -1,-1,data,dataSize,
                                    -1,-1,&mDisPlayProcessBuffer[index*dstSize],dstSize);  
        memset(&processInfo,0x00,sizeof(IMAGE_PROCESS_INFO_T));

        //memset(_decode_arg->rga_buf,0x00,(1920/2)*(1080/2)*4); 
        processInfo.srcRecSize.width = width;
        processInfo.srcRecSize.hight = height;
        processInfo.dstRecSize.width = CODEC_ALIGN(surface->rect[index].u32Width-4,4);
        processInfo.dstRecSize.hight = CODEC_ALIGN(surface->rect[index].u32Height-4,4);


        processInfo.Img_ProcessFlg = IMAGE_PROCESS_ZOOM;
        processInfo.srcFormat = MPP_FRAME_TYPE_YUV420SP;
        processInfo.dstFormat = MPP_FRAME_TYPE_YUV420SP;
        if(MPP_RET_CODE_OK != mProcess->image_process(mProcess->ctx,&processInfo)){
            cout<<"image_process err"<<endl;
             return MPP_RET_CODE_FAILURE;
        }
               
        
        overlayParam param;
        memset(&param,0x00,sizeof(overlayParam));

        param.bm_w = mScreen.width;
        param.bm_h = mScreen.height;
        param.bm_buff = framebuffer;

        param.top_w =processInfo.dstRecSize.width;//  surface->rect[index].u32Width -4;
        param.top_h = processInfo.dstRecSize.hight;//surface->rect[index].u32Height -4;
        param.top_buff = &mDisPlayProcessBuffer[index*dstSize];//(uint8_t *)data

        if(surface->rect[index].s32X+surface->rect[index].u32Width == mScreen.width)
            param.pos_x = surface->rect[index].s32X+2;
        else if(surface->rect[index].s32X+surface->rect[index].u32Width >mScreen.width)
             return MPP_RET_CODE_FAILURE;
        else 
            param.pos_x = surface->rect[index].s32X;


        if(surface->rect[index].s32Y+surface->rect[index].u32Height ==  mScreen.height)    
            param.pos_y = surface->rect[index].s32Y+2;
        else if(surface->rect[index].s32Y+surface->rect[index].u32Height >  mScreen.height) 
            return MPP_RET_CODE_FAILURE;
        else
            param.pos_y = surface->rect[index].s32Y;

        
        if(0!= pixel_format_nv12_overlay(&param))
            return MPP_RET_CODE_FAILURE;
         surface->fd =-1;//fd;
        return mpp_display_draw(surface, 0,0,mScreen.width ,mScreen.height,framebuffer_index) ;  

        
}   
RET_CODE mpp_display::mpp_surface_destroy_rects(MPP_SURFACE_ATTR *surface)
{
    MPP_SURFACE_RECT_S  *rect = NULL;
     uint32_t count_rects = 0;
    if(!surface || !surface->rect)
        return MPP_RET_CODE_FAILURE;
    free(surface->rect);
    surface->rect = NULL;
    surface->count_rect = 0;
    return MPP_RET_CODE_OK;
}                                                                                                                              

RET_CODE mpp_display::mpp_display_draw(MPP_SURFACE_ATTR *surface,
                                    uint32_t position_x ,uint32_t position_y,
                                    uint32_t width ,uint32_t height,uint32_t BufferIndex)
{
    int  ret = 0;
    if(surface && screen){
        
        //   ret = screen->display_img(surface->position_x ,surface->position_y ,
        //                                                 surface->width,surface->height,
        //                                                 surface->format,surface->fd);  
        ret = screen->display_img(position_x ,position_y ,
                                                    width,height,
                                                    surface->format,surface->fd,BufferIndex);  
          if(ret )
             return MPP_RET_CODE_FAILURE; 

        surface->position_x = position_x;
        surface->position_y = position_y;
        surface->width = width;
        surface->height = height;


        return MPP_RET_CODE_OK;
    }
    return MPP_RET_CODE_FAILURE;       
}
RET_CODE mpp_display::mpp_display_destroy_surface(MPP_SURFACE_ATTR **surface)
{
    if(*surface)
        delete *surface;
    return MPP_RET_CODE_OK;
}
RET_CODE mpp_display::mpp_display_deinit()
{
    // if(m_screens_handle){
    //     delete m_screens_handle;
    //     m_screens_handle = NULL;
    // }
        
    // if(show){
    //     delete show;
    //     show = NULL;
    // }
    if(screen){
        delete screen;
        screen = NULL;
    }
    if(buffer){
        delete buffer;
        buffer = NULL;
    }
    if(mDisPlayProcessBuffer){
        delete mDisPlayProcessBuffer;
        mDisPlayProcessBuffer = NULL;
    }

        
    return MPP_RET_CODE_OK;
}
/**************************************************************************************************************/













typedef struct
{
    mpp_display *display;

}mpp_display_contex;


static RET_CODE mpp_display_init(void *cxt,uint32_t width ,uint32_t height,uint32_t vrefresh)
{
    mpp_display_contex *contex = (mpp_display_contex*)cxt;
    mpp_display *display = new mpp_display();
    if(display  ){
        if(contex){
            contex->display = display;
            return display->mpp_display_init( width , height, vrefresh);
            
        }else{
            delete display;
        }
    }
    return MPP_RET_CODE_FAILURE;
}

static MPP_SURFACE_ATTR *mpp_create_surface(void *cxt,FRAME_TYPE format,uint32_t position_x,uint32_t position_y,uint32_t width,uint32_t height)
{
    mpp_display_contex *contex = (mpp_display_contex*)cxt;
    
    if(!contex &&!contex->display)
        return NULL;
    
    return contex->display->mpp_display_create_surface(format, position_x, position_y, width, height);

}

static RET_CODE mpp_draw(void *cxt,MPP_SURFACE_ATTR *surface,
                                    uint32_t position_x ,uint32_t position_y,
                                    uint32_t width ,uint32_t height)
{
    mpp_display_contex *contex = (mpp_display_contex*)cxt;
    if(!contex->display)
         return MPP_RET_CODE_FAILURE;
   return contex->display->mpp_display_draw(surface,position_x,position_y,width,height);
}

static RET_CODE mpp_destroy_surface(void *cxt,MPP_SURFACE_ATTR *surface)
{
    mpp_display_contex *contex = (mpp_display_contex*)cxt;
    if(!contex->display)
        return MPP_RET_CODE_FAILURE;
    return contex->display->mpp_display_destroy_surface(&surface);
}



static RET_CODE mpp_surface_create_rects(void *cxt,MPP_SURFACE_ATTR *surface,
                                                                    uint32_t count_rects,MPP_SURFACE_RECT_S  *rect)
{
    mpp_display_contex *contex = (mpp_display_contex*)cxt;
    if(!contex->display)
        return MPP_RET_CODE_FAILURE;
    return contex->display->mpp_surface_create_rects(surface,count_rects,rect);

}

static RET_CODE mpp_surface_rect_draw(void *cxt,MPP_SURFACE_ATTR *surface,
                                                            uint32_t index,void *data,uint32_t width ,uint32_t height)
{
    mpp_display_contex *contex = (mpp_display_contex*)cxt;
    if(!contex->display)
        return MPP_RET_CODE_FAILURE;
    return contex->display->mpp_surface_rect_draw(surface,index,data,width,height);
}

static RET_CODE mpp_surface_destroy_rects(void *cxt,MPP_SURFACE_ATTR *surface)
{
    mpp_display_contex *contex = (mpp_display_contex*)cxt;
    if(!contex->display)
        return MPP_RET_CODE_FAILURE;
    return contex->display->mpp_surface_destroy_rects(surface); 
}




static     MPP_SCREEN_ATTR *mpp_display_get_screen_attr(void *cxt)
{
    mpp_display_contex *contex = (mpp_display_contex*)cxt;
    if(!contex->display)
        return NULL;
    return contex->display->mpp_display_get_screen_attr();
}

static RET_CODE mpp_display_deinit(void *cxt)
{
    mpp_display_contex *contex = (mpp_display_contex*)cxt;
    if(contex){
        if(contex->display ){
            contex->display->mpp_display_deinit();
            delete contex->display;
            contex->display = NULL;
            return MPP_RET_CODE_OK;
        }
    }
     return MPP_RET_CODE_FAILURE;
}



MPP_ITC_DISPLAY_FUNTION_T * create_display_process(void)
{
    MPP_ITC_DISPLAY_FUNTION_T * display_funtion = new MPP_ITC_DISPLAY_FUNTION_T();
    if(display_funtion){
        mpp_display_contex *contex = new mpp_display_contex();
        if(contex){
                display_funtion->cxt                            = (void *)contex;
                display_funtion->mpp_display_init      = mpp_display_init;
                display_funtion->mpp_display_deinit  = mpp_display_deinit;

                display_funtion->mpp_create_surface = mpp_create_surface;
                display_funtion->mpp_surface_draw                = mpp_draw;
                display_funtion->mpp_destroy_surface= mpp_destroy_surface;
                display_funtion->mpp_surface_create_rects = mpp_surface_create_rects;
                display_funtion->mpp_surface_rect_draw     =  mpp_surface_rect_draw;
                display_funtion->mpp_surface_destroy_rects = mpp_surface_destroy_rects;

                display_funtion->mpp_display_get_screen_attr = mpp_display_get_screen_attr;




                return display_funtion;
        }else{
            if(display_funtion)
                delete display_funtion;
        }
    }
    return NULL;
}
void display_process_destroy(MPP_ITC_DISPLAY_FUNTION_T * display)
{
    if(display){
            mpp_display_contex *contex = (mpp_display_contex *)display->cxt;
            if(contex->display){
                contex->display->mpp_display_deinit();
                delete contex->display;
                contex->display = NULL;
            }
            delete display;
            display = NULL;            
    }
}



